﻿namespace SimplyCleverMiddleTier
{
    using System;
    using System.Configuration;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Diagnostics.Contracts;
    using System.IO;
    using System.Data;

    [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")]
    public static class Db
    {
       

        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0"), DebuggerNonUserCode]
        public static T GetValue<T>(this DbDataReader reader, string columnName)
        {
            int columnOrdinal = reader.GetOrdinal(columnName);

            if (columnOrdinal < 0)
                return default(T);

            var sqlValue = reader[columnOrdinal];

            if (sqlValue == null || sqlValue == DBNull.Value)
                return default(T);

            var t = typeof(T);

            if (t.IsEnum)
            {
                return (T)Enum.Parse(t, Convert.ToString(sqlValue));
            }

            return (T)Convert.ChangeType(sqlValue, typeof(T));
        }

        public static void ReadFrom(string connectionName, bool useTransaction, Action<DbCommand> commandBlock, params Action<DbDataReader>[] readerBlock)
        {
            DbProviderFactory factory = null;

            factory = DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings[connectionName].ProviderName);

            DbTransaction transaction = null;

            using (DbConnection connection = factory.CreateConnection())
            {
                connection.ConnectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString; ;
                connection.Open();

                if (useTransaction)
                    transaction = connection.BeginTransaction();

                using (DbCommand command = connection.CreateCommand())
                {
                    if (useTransaction)
                        command.Transaction = transaction;

                    commandBlock(command);

                    using (DbDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                    {
                        int orderNumber = 0;

                        do
                        {
                            while (reader.Read())
                                readerBlock[orderNumber](reader);

                            orderNumber++;
                        } while (reader.NextResult());
                    }
                }
                if (useTransaction)
                    transaction.Commit();
            }
        }

       
        public static void ExecuteCommands(string connectionName, bool useTransaction, Action<DbCommand> commandBlock)
        {
           DbProviderFactory factory = null;

            factory = DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings[connectionName].ProviderName);

            DbTransaction transaction = null;

            using (DbConnection connection = factory.CreateConnection())
            {
                connection.ConnectionString = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
                connection.Open();

                if (useTransaction)
                    transaction = connection.BeginTransaction();

                using (DbCommand command = connection.CreateCommand())
                {
                    if (useTransaction)
                        command.Transaction = transaction;

                    commandBlock(command);
                }
                if (useTransaction)
                    transaction.Commit();
            }
        }


        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0")]
        public static void AddParameter<T>(this DbCommand command, string name, T parameterValue)
        {
            DbParameter dbParameter;
            dbParameter = command.CreateParameter();
            dbParameter.ParameterName = name;
            dbParameter.Value = parameterValue;
            command.Parameters.Add(dbParameter);
        }


        [DebuggerNonUserCode]
        static byte[] GetBytes(this IDataReader reader, int position)
        {
            const int CHUNK_SIZE = 16;
            byte[] buffer = new byte[CHUNK_SIZE];
            long bytesRead;
            long fieldOffset = 0;

            using (MemoryStream stream = new MemoryStream())
            {
                while ((bytesRead = reader.GetBytes(position, fieldOffset, buffer, 0, buffer.Length)) > 0)
                {
                    byte[] actualRead = new byte[bytesRead];
                    Buffer.BlockCopy(buffer, 0, actualRead, 0, (int)bytesRead);
                    stream.Write(actualRead, 0, actualRead.Length);
                    fieldOffset += bytesRead;
                }
                return stream.ToArray();
            }
        }

        [DebuggerNonUserCode]
        public static byte[] GetBytes(this IDataReader reader, string columnName)
        {
            int index = reader.GetOrdinal(columnName);

            return GetBytes(reader, index);
        }
    }
}